﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace WeChatParking.Core.Payment.WeixinPay
{
    public class WxpayClient
    {
    
        private WebUtils webUtils; 

        public WxpayClient()
        {    
            this.webUtils = new WebUtils();
        }

        public void SetTimeout(int timeout)
        {
            webUtils.Timeout = timeout;
        }


        /// <summary>
        /// 统一下单接口
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public WxpayUnifiedOrderResponse ExecuteUnifiedOrderRequest(WxpayUnifiedOrderRequest request)
        {
            var log = new Entity.log();
            log.pay_type = (short)WeChatParking.Entity.Enums.PayType.Weixin;
           
            request.Sign = WxpayHelper.GetSign(request.GetParameters(), WxpayConfig.WxPayKey);
            string parametersStr = XmlHelper.XmlSerialize(request);

            string body = webUtils.DoPost(WxpayConfig.UnifiedOrderUrl,parametersStr,WxpayConfig.Charset);

            WxpayUnifiedOrderResponse rsp = XmlHelper.XmlDeserialize<WxpayUnifiedOrderResponse>(body);

            log.api_name = "统一下单请求";
            log.request = parametersStr;
            log.response = body;
        
            if (rsp.ReturnCode == "FAIL")
            {
                log.message = "微信统一下单请求失败";
                PayUtil.WriteOrderPayLog(log);
                return null;
            }

            //验签
            bool check = WxpayHelper.CheckSign(rsp.GetParameters(),WxpayConfig.WxPayKey, rsp.Sign);
            if (!check)
            {
                log.message = "微信统一下单返回值验签错误" ;
                PayUtil.WriteOrderPayLog(log);
                return null;
            }

            PayUtil.WriteOrderPayLog(log);
            return rsp;
           
        }

        /// <summary>
        /// 查询订单接口
        /// </summary>
        public WxpayQueryOrderResponse ExecuteQueryOrderRequest(string outTradeNo)
        {
            var request = new WxpayQueryOrderRequest()
            {
                OutTradeNo = outTradeNo
            };
            return ExecuteQueryOrderRequest(request);
        }

        public WxpayQueryOrderResponse ExecuteQueryOrderRequestByTransactionID(string transactionId)
        {
            var request = new WxpayQueryOrderRequest()
            {
                TransactionID = transactionId
            };
            return ExecuteQueryOrderRequest(request);
        }

        /// <summary>
        /// 查询订单接口
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        public WxpayQueryOrderResponse ExecuteQueryOrderRequest(WxpayQueryOrderRequest request)
        {
            var log = new Entity.log();
            log.pay_type = (short)WeChatParking.Entity.Enums.PayType.Weixin;

            request.Sign = WxpayHelper.GetSign(request.GetParameters(), WxpayConfig.WxPayKey);

            string parametersStr = XmlHelper.XmlSerialize(request);

            string body = webUtils.DoPost(WxpayConfig.QueryOrderUrl, parametersStr, WxpayConfig.Charset);

            WxpayQueryOrderResponse rsp = XmlHelper.XmlDeserialize<WxpayQueryOrderResponse>(body);

            log.api_name = "微信查询订单请求";
            log.request = parametersStr;
            log.response = body;
           
            if (rsp.ReturnCode == "FAIL")
            {
                log.message = "微信查询订单请求失败";
                PayUtil.WriteOrderPayLog(log);
                return null;
            }

            if (rsp.ResultCode != "SUCCESS" || rsp.TradeState != "SUCCESS")
            {
                log.message = "微信该订单支付未支付或正在支付中，详情请根据TradeState查询微信开发文档";
                PayUtil.WriteOrderPayLog(log);
                return null;
            } 

            //验签
            bool check = WxpayHelper.CheckSign(body, WxpayConfig.WxPayKey, request.AppID, request.MchID);
            if (!check)
            {
                log.message = "微信查询订单返回值验签错误";
                PayUtil.WriteOrderPayLog(log);
                return null;
            }
            else
            {
                PayUtil.WriteOrderPayLog(log);
                return rsp;
            }
        }


        /// <summary>
        /// 生成静态二维码链接
        /// </summary>
        /// <param name="productID"></param>
        /// <returns></returns>
        public static string GetNativeLink(string productID)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            string timeStamp =WxpayHelper.GetTimeStamp();
            string nonceStr = WxpayHelper.GetNoncestr();
            parameters.Add("appid",WxpayConfig.AppID);
            parameters.Add("mch_id", WxpayConfig.MchID);
            parameters.Add("nonce_str",nonceStr);
            parameters.Add("product_id",productID);
            parameters.Add("time_stamp", timeStamp);
            string sign = WxpayHelper.GetSign(parameters, WxpayConfig.WxPayKey);
            return string.Format(WxpayConfig.NativeLink,WxpayConfig.AppID,WxpayConfig.MchID,nonceStr,productID,timeStamp,sign);
        }

        private static object getAccessTokenLocker = new object();

        /// <summary>
        /// 获取access_token
        /// access_token是公众号的全局唯一票据，公众号调用各接口时都需使用access_token。
        /// </summary>
        /// <param name="forceRefresh">是否强制刷新</param>
        public string GetAccessToken(bool forceRefresh = false)
        {
            string accessToken = forceRefresh ? string.Empty : WeChatParking.Util.Cache.GetCache<string>("WXAccessToken");
            if (!string.IsNullOrEmpty(accessToken))
            {
                Core.LogHelper.Debug(accessToken, "获取access_token(缓存)");
                return accessToken;
            }
            lock (getAccessTokenLocker)
            {
                //预防并发同时获取access_token
                accessToken = WeChatParking.Util.Cache.GetCache<string>("WXAccessToken");

                if (string.IsNullOrWhiteSpace(accessToken))
                {
                    string accessLink = string.Format(WxpayConfig.AccessTokenLink, WxpayConfig.AppID, WxpayConfig.AppSecret);
                    string result = webUtils.DoGet(accessLink, "UTF-8");

                    Core.LogHelper.Debug(result, "获取access_token");

                    try
                    {
                        WxAccessTokenResult ret = WeChatParking.Util.Conv.ParseJson<WxAccessTokenResult>(result);
                        if(ret == null || ret.errcode != 0)
                        {
                            Exception ex = new Exception("获取access_token出错，具体错误：" + result);
                            LogHelper.Error(ex);
                            throw ex;
                        }
                        accessToken = ret.access_token;
                    }
                    catch(Exception ex)
                    {
                        LogHelper.Error(ex);
                        throw ex;
                    }

                    //微信access_token有效期为7200，这里设置7000，避免时间差
                    WeChatParking.Util.Cache.SetCache("WXAccessToken", accessToken, DateTime.Now.AddSeconds(7000));
                }
            }
            return accessToken;
        }

        private static object getJsapiTicketLocker = new object();
        /// <summary>
        /// 获取jsapi_ticket
        /// jsapi_ticket是公众号用于调用微信JS接口的临时票据。
        /// </summary>
        /// <returns></returns>
        public string GetJsapiTicket()
        {
            string ticket = WeChatParking.Util.Cache.GetCache<string>("WXJsApiTicket");
            if (!string.IsNullOrEmpty(ticket))
            {
                return ticket;
            }
            string accessToken = GetAccessToken();

            lock (getJsapiTicketLocker)
            {
                ticket = WeChatParking.Util.Cache.GetCache<string>("WXJsApiTicket");
                if (!string.IsNullOrEmpty(ticket))
                {
                    return ticket;
                }

                string ticketUrl = string.Format(WxpayConfig.JsApiTicketLink, accessToken);
                string result = webUtils.DoGet(ticketUrl, "UTF-8");

                Core.LogHelper.Debug(result, "获取jsapi_ticket");

                if (result.Contains("ok"))
                {
                    WxPayJsapiTicket ticketModel = WeChatParking.Util.Conv.ParseJson<WxPayJsapiTicket>(result);
                    //微信jsapi_ticket有效期为7200，这里设置7000，避免时间差
                    WeChatParking.Util.Cache.SetCache("WXJsApiTicket", ticketModel.ticket, DateTime.Now.AddSeconds(7000));
                    return ticketModel.ticket;
                }
                else
                {
                    Exception ex = new Exception("获取JsApiTicket出错，具体错误：" + result);
                    LogHelper.Error(ex);
                    throw ex;
                }
            }          
        }

        /// <summary>
        ///  获取当前页面调用JSAPI的全局配置信息
        /// </summary>
        /// <returns></returns>
        public WxpayJsConfigModel GetJSConfigModel(string url)
        {
            WxpayJsConfigModel config = new WxpayJsConfigModel();
            //string Url2= HttpContext.Current.Request.Url.ToString();
            config.Url = url.Split('#')[0];
            config.NonceStr = WxpayHelper.GetNoncestr();
            config.TimeStamp = WxpayHelper.GetTimeStamp();
            config.JsapiTicket = GetJsapiTicket();
            config.Signature = WxpayHelper.GetSha1Sign(config.GetParameters());
            return config;
        }

        /// <summary>
        /// 服务号推送模板消息
        /// </summary>
        public Entity.WxMsgApiResult SendMessage(string openId, string templateId, string url, object data)
        {
            var apiUrl = string.Format(
                "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}",
                GetAccessToken()
                );

            var msg = new
            {
                touser = openId,
                template_id = templateId,
                url = url,
                data = data
            };

            var repJson = webUtils.DoPost(apiUrl, Util.Conv.ToJson(msg), "UTF-8");
            var result = Util.Conv.ParseJson<Entity.WxMsgApiResult>(repJson);
            return result;
        }
    }
}
